﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Build.Framework;
using System.IO;
using System.Text.RegularExpressions;
using LightPersistTests.MetaGenerator.Properties;
using Microsoft.Build.Utilities;

namespace LightPersistTests.MetaGenerator
{
	[LoadInSeparateAppDomainAttribute]
	public class GenerateFixtures : AppDomainIsolatedTask
	{
		[Required]
		public ITaskItem[] Sources { get; set; }

		public string OutputFile { get; set; }

		public override bool Execute()
		{
			// Debugger.Launch();
			WriteLog("GenerateFixtures.Execute...");
			//var outFN = Path.GetFileName(OutputFile);
			var files = Sources.Where(x => x.ItemSpec.EndsWith(".cs") 
				// && Path.GetFileName(x.ItemSpec) != outFN
				).ToArray();

			WriteLog("Generate Triple Fixtures...");

			foreach (var item in files.Where(ItemAllowed))
			{
				GenerateTripleFixtures(item);
			}

			WriteLog("Generate Xml Storage Acceptance Tests...");

			foreach (var item in files.Where(ItemAllowed))
			{
				GenerateAcceptanceXmlStorageTests(item);
			}

			var code = _codeForHolder.ToString();
			if (File.ReadAllText(OutputFile) != code)
			{
				File.SetAttributes(OutputFile, File.GetAttributes(OutputFile) & ~FileAttributes.ReadOnly);
				File.WriteAllText(OutputFile, code);
				WriteLog("Generated: " + OutputFile);
			}
			else
			{
				WriteLog("Generation is up to date: " + OutputFile);
			}

			return true;
		}

		private void WriteLog(string msg)
		{
			Log.LogMessage(MessageImportance.High, "TestGen: " + msg);
		}

		static readonly Regex _xmlAcceptanceRx = new Regex(@"(?i)\[TestClass\]\s*public\s+class\s+(?'ClassName'[a-z01-9_]+)\s*:\s*(?'BaseName'[a-z01-9_]+)", RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.CultureInvariant);


		private void GenerateAcceptanceXmlStorageTests(ITaskItem item)
		{
			//LogLogMessage(MessageImportance.High, "\tFile: " + item.ItemSpec);

			const string suffix = "_gen";

			foreach(Match match in _xmlAcceptanceRx.Matches(File.ReadAllText(item.ItemSpec)))
			{
				// WriteLog("! " + match.Groups["ClassName"].Value);
				var className = match.Groups["ClassName"].Value;
				var baseClassName = match.Groups["BaseName"].Value;

				if (className.EndsWith(suffix) || string.IsNullOrEmpty(baseClassName) || baseClassName == "FixtureMsTest")
				{
					continue;
				}

				_codeForHolder.AppendFormat(Resources.Pattern_XmlStorageAcceptance, className, className);
			}
		}

		readonly StringBuilder _codeForHolder = new StringBuilder(Resources.HolderHeader);

		static readonly Regex _tripleRx = new Regex(@"(?im)(#pragma\s+TestGen\s+(?'pragma'.*)$\s*)?\[TestClass\]\s*public class (?'ClassName'[a-z01-9_]+)\s*:\s*TripleTestsBase", RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.CultureInvariant);

		static bool ItemAllowed(ITaskItem item)
		{
			return true;
			// output file should be readable to aply several generation on each other (such as xmlStrorage after tripleTestBase)
			// return Path.GetFileName(item.ItemSpec) != Path.GetFileName(OutputFile);
		}

		private void GenerateTripleFixtures(ITaskItem item)
		{
			var content = ReadFile(item.ItemSpec);
			var matches = _tripleRx.Matches(content);

			// LogLogMessage(MessageImportance.High, "# Matches for TripleFixtures: " + matches.Count);

			foreach (Match match in matches)
			{
				// Log("! " + match.Groups["ClassName"].Value);
				bool differentTransaction = true, differentContext = true, differentTransactionUndoRedo = true;

				var pragma = match.Groups["pragma"].Value.Trim();
				if(!string.IsNullOrEmpty(pragma))
				{
					WriteLog("PRAGMA: " + pragma + " in " + match.Groups["ClassName"].Value);
					var pragmaParse = Regex.Match(pragma, @"(?'cmd'\w+)(\s(?'args'.*))?");
					switch(pragmaParse.Groups["cmd"].Value.ToLowerInvariant())
					{
						case "ignore":
							continue;
						case "ignore_different_transaction":
							differentTransaction = false;
							break;
						case "ignore_different_context":
							differentContext = false;
							break;
						case "ignore_undoredo":
						case "ignore_undo_redo":
							differentTransactionUndoRedo = false;
							break;
						default:
							throw new ApplicationException("UNKNOWN PRAGMA DIRRECTIVE: " + pragma);
					}
				}

				// LogLogMessage(MessageImportance.High, "\tTestClass: " + match.Groups["ClassName"].Value);
				var className = match.Groups["ClassName"].Value;
				if(differentTransaction)
				{
					_codeForHolder.AppendFormat(Resources.TripleFixturePattern_transaction, className);
				}
				if(differentTransactionUndoRedo)
				{
					_codeForHolder.AppendFormat(Resources.TripleFixturePattern_UndoRedo, className);
				}
				if(differentContext)
				{
					_codeForHolder.AppendFormat(Resources.TripleFixturePattern_context, className);
				}
			}
		}

		private string ReadFile(string fileName)
		{
			if(Path.GetFileName(fileName) == Path.GetFileName(OutputFile))
			{
				return _codeForHolder.ToString();
			}
			return File.ReadAllText(fileName);
		}
	}
}
